// ============================================================
// Vite配置文件 - 用于配置项目的构建、开发服务器和插件系统
// ============================================================

// 导入Node.js核心模块 - 文件系统操作相关
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"
// 导入Node.js URL处理模块，用于将文件URL转换为路径
import { fileURLToPath } from "node:url"

// 导入环境变量类型定义
import type { env as EnvType } from "@follow/shared/env.desktop"
// 导入Vite插件 - 为旧浏览器提供支持
import legacy from "@vitejs/plugin-legacy"
// 导入HTML压缩工具，用于优化生产构建
import { minify as htmlMinify } from "html-minifier-terser"
// 导入终端彩色输出工具，用于美化控制台日志
import { cyan, dim, green } from "kolorist"
// 导入HTML解析工具，用于处理HTML文档
import { parseHTML } from "linkedom"
// 导入路径处理工具，用于处理文件路径
import { join, resolve } from "pathe"
// 导入Vite相关类型定义
import type { PluginOption, ResolvedConfig, ViteDevServer } from "vite"
// 导入Vite核心配置函数
import { defineConfig, loadEnv } from "vite"
// 导入打包体积分析插件
import { analyzer } from "vite-bundle-analyzer"
// 导入SSL证书生成插件
import mkcert from "vite-plugin-mkcert"
// 导入PWA(渐进式Web应用)插件
import { VitePWA } from "vite-plugin-pwa"
// 导入路由自动生成插件
import { routeBuilderPlugin } from "vite-plugin-route-builder"

// 导入自定义配置文件 - 基础渲染配置
import { viteRenderBaseConfig } from "./configs/vite.render.config"
// 导入自定义插件 - 依赖分块插件，用于优化打包
import { createDependencyChunksPlugin } from "./plugins/vite/deps"
// 导入自定义插件 - HTML注入插件，用于向HTML注入内容
import { htmlInjectPlugin } from "./plugins/vite/html-inject"
// 导入自定义插件 - 多语言插件，处理国际化
import { localesPlugin } from "./plugins/vite/locales"
// 导入自定义插件 - 清单处理插件
import manifestPlugin from "./plugins/vite/manifest"
// 导入自定义插件 - 平台特定导入插件
import { createPlatformSpecificImportPlugin } from "./plugins/vite/specific-import"

// 设置当前目录路径，使用URL和fileURLToPath获取绝对路径
const __dirname = fileURLToPath(new URL(".", import.meta.url))
// 判断是否为CI环境(持续集成环境)
const isCI = process.env.CI === "true" || process.env.CI === "1"
// 设置渲染器根目录，指向layer/renderer文件夹
const ROOT = resolve(__dirname, "./layer/renderer")

// 创建一个自定义插件 - 用于修改开发服务器的URL输出
const devPrint = (): PluginOption => ({
  name: "dev-print", // 插件名称，用于标识
  // configureServer钩子函数 - 配置开发服务器
  // 1. 1.
  //    接收参数 ：接收 Vite 的开发服务器实例 server
  // 2. 2.
  //    保存原始方法 ：把服务器的 printUrls 方法保存到 _printUrls 变量
  // 3. 3.
  //    重写方法 ：创建一个新的 printUrls 函数，实现了：
  //    - 调用原始的 _printUrls() 方法（保留原有功能）
  //    - 添加额外的调试 URL 信息到控制台输出
  // ### 技术原理
  // - 这是一种 装饰器模式 的应用 - 不直接修改原始代码，而是在保留原功能的基础上增加新功能
  // - 通过 函数重写 技术实现了对 Vite 开发服务器行为的定制
  // - 使用了 闭包 来保存原始方法的引用
  // ### 最终效果
  // 当你运行 Vite 开发服务器时，除了默认显示的本地和网络 URL 外，还会额外显示两行调试代理 URL：

  // - Production debug URL
  // - Development debug URL
  configureServer(server: ViteDevServer) {
    // 保存原始的printUrls方法到变量中
    const _printUrls = server.printUrls
    // 重写printUrls方法
    server.printUrls = () => {
      _printUrls() // 调用原始方法，保留默认输出
      // 添加自定义URL信息到控制台输出
      console.info(
        // - 前面还有 green("➜") 作为前缀标记
        // - 被包裹在 cyan() 函数中，用于在终端输出青色文本，提高可读性
        // - 前面有 dim("Development debug") ，使用灰色显示标签说明
        `  ${green("➜")}  ${dim("Production debug")}: ${cyan("https://app.folo.is/__debug_proxy")}`,
      )
      console.info(
        `  ${green("➜")}  ${dim("Development debug")}: ${cyan(
          "https://dev.follow.is/__debug_proxy",
        )}`,
      )
      console.info(`  ${green("➜")}  ${dim("Production debug")}: ${cyan("开始动手修改打印日志")}`)
    }
  },
})

// 检查是否为Web构建模式
const isWebBuild = process.env.WEB_BUILD === "1"
// 输出构建类型信息到控制台
// eslint-disable-next-line no-console
console.log(green("Build type:"), isWebBuild ? "Web" : "Unknown")

// 定义代理配置对象，用于处理特定路径的请求代理
const proxyConfig = {
  target: "http://localhost:2234", // 代理目标服务器地址
  changeOrigin: true, // 修改请求头中的Origin，防止跨域问题
  selfHandleResponse: true, // 启用自定义响应处理
  // 配置代理行为的回调函数
  configure: (proxy, _options) => {
    // 添加代理启动日志
    // eslint-disable-next-line no-console
    console.log(
      green("proxyConfig started:"),
      `Target: ${proxyConfig.target}, Paths: /login, /forget-password, /reset-password, /register, /share`,
    )
    // 监听代理的响应事件 - 当代理服务器收到目标服务器响应时触发
    proxy.on("proxyRes", (proxyRes, req, res) => {
      // 创建空数组用于存储响应数据块
      const body = [] as any[]

      // 监听数据事件 - 当接收到响应数据块时
      proxyRes.on("data", (chunk: any) => {
        // 将接收到的数据块添加到数组中
        body.push(chunk)
      })

      // 监听响应结束事件 - 当所有数据接收完成后触发
      proxyRes.on("end", () => {
        // 将所有数据块合并为Buffer，转换为字符串，然后解析HTML
        const html = parseHTML(Buffer.concat(body).toString())
        // 获取解析后的HTML文档对象
        const doc = html.document

        // 处理所有script标签 - 修改其src属性指向本地开发服务器
        const $scripts = doc.querySelectorAll("script")
        $scripts.forEach((script) => {
          // 获取原始的src属性值
          const src = script.getAttribute("src")
          if (src) {
            // 添加日志输出src信息
            // eslint-disable-next-line no-console
            console.log(`[Proxy] Modifying script src: ${src} → http://localhost:2234${src}`)
            // 修改src属性，将其指向本地2234端口
            script.setAttribute("src", `http://localhost:2234${src}`)
          }
        })

        // 处理所有link标签 - 类似地修改href属性
        const $links = doc.querySelectorAll("link")
        $links.forEach((link) => {
          // 获取原始的href属性值
          const href = link.getAttribute("href")
          if (href) {
            // 添加日志输出href信息
            // eslint-disable-next-line no-console
            console.log(`[Proxy] Modifying link href: ${href} → http://localhost:2234${href}`)
            // 修改href属性，将其指向本地2234端口
            link.setAttribute("href", `http://localhost:2234${href}`)
          }
        })

        // 设置响应头 - 确保浏览器正确解析HTML内容和字符集
        res.setHeader("Content-Type", "text/html; charset=utf-8")

        // 将修改后的HTML文档转换为字符串
        const modifiedHtml = doc.toString()
        // 结束响应并发送修改后的HTML内容
        res.end(modifiedHtml)
      })
    })
  },
}

// Vite配置的默认导出函数 - 接收mode参数(development/production)
export default ({ mode }) => {
  // 加载环境变量 - 从mode和当前工作目录加载
  const env = loadEnv(mode, process.cwd())
  // 将环境变量强制转换为指定的类型
  const typedEnv = env as typeof EnvType

  // 返回Vite配置对象 - 使用defineConfig函数确保TypeScript类型提示和自动补全
  return defineConfig({
    // 扩展基础配置 - 合并从viteRenderBaseConfig导入的共享配置
    ...viteRenderBaseConfig,

    // 项目根目录设置 - 指定Vite查找文件的根目录
    root: ROOT,

    // 基础路径设置 - 用于生成静态资源路径的前缀
    base: "/",

    // 环境变量目录设置 - 指定.env文件所在目录
    // 这里的 resolve 是 Node.js 的 path 模块 中的一个函数， 不是 Vite 配置对象中的 resolve 选项。它的具体作用是：

    // 1. 1.
    //    路径解析功能 ：将相对路径解析为绝对路径，确保无论从哪个目录运行构建命令，都能正确找到输出目录
    // 2. 2.
    //    参数说明 ：

    //    - __dirname ：Node.js 中的内置变量，表示当前执行脚本所在的目录（即 vite.config.ts 文件所在目录）
    //    - "out/web" ：相对于 __dirname 的子目录路径
    // 3. 3.
    //    实际效果 ：将输出目录设置为 /Users/mac/project/folo/apps/desktop/out/web ，这个路径是确定的、绝对的
    // 4. 4.
    //    为什么使用 ：使用 resolve 函数可以避免相对路径的问题，确保构建产物始终输出到正确的位置，无论 Vite 命令是从哪个目录执行的
    envDir: resolve(__dirname, "."),

    // 构建配置 - 控制应用打包行为
    build: {
      // 输出目录路径 - 构建产物存放位置
      outDir: resolve(__dirname, "out/web"),

      // 构建目标 - 指定生成的代码兼容ES2022特性
      target: "ES2022",

      // 是否生成sourcemap - 仅在CI环境中生成，帮助调试生产环境错误
      sourcemap: isCI,

      // Rollup配置 - Vite底层的打包工具配置
      rollupOptions: {
        // 入口文件配置
        input: {
          // 主入口文件 - 项目的HTML入口点
          main: resolve(ROOT, "/index.html"),
        },
      },
    },

    // 开发服务器配置 - 本地开发环境的服务器设置
    server: {
      // 允许从外部访问开发服务器 - 使其他设备可通过IP访问
      host: true,

      // 开发服务器端口 - 本地开发服务器运行在2233端口
      port: 2233,

      // 文件监听配置 - 控制哪些文件变更会触发重新构建
      watch: {
        // 忽略监听的目录 - 这些目录的变化不会触发重新构建
        ignored: ["**/dist/**", "**/out/**", "**/public/**", ".git/**"],
      },

      // 启用跨域请求支持 - 允许跨域请求
      cors: true,

      // 代理配置 - 将特定请求代理到其他服务器
      proxy: {
        // 将/login路径的请求代理到2234端口 - 使用之前定义的proxyConfig配置
        "/login": proxyConfig,
        // 将/forget-password路径的请求代理到2234端口
        "/forget-password": proxyConfig,
        // 将/reset-password路径的请求代理到2234端口
        "/reset-password": proxyConfig,
        // 将/register路径的请求代理到2234端口
        "/register": proxyConfig,
        // 将/share路径的请求代理到2234端口
        "/share": proxyConfig,

        // 根据环境变量配置可选的开发代理 - 条件展开语法
        ...(env.VITE_DEV_PROXY
          ? {
              // 如果环境变量VITE_DEV_PROXY存在
              // 动态键名 - 使用环境变量的值作为代理路径
              [env.VITE_DEV_PROXY]: {
                // 代理目标地址 - 从环境变量获取
                target: env.VITE_DEV_PROXY_TARGET,
                // 修改请求头中的Origin - 防止跨域问题
                changeOrigin: true,
                // 路径重写函数 - 从请求路径中移除代理前缀
                rewrite: (path) => path.replace(new RegExp(`^${env.VITE_DEV_PROXY}`), ""),
              },
            }
          : {}), // 如果环境变量不存在则使用空对象
      },
    },

    // 解析配置 - 控制模块解析行为
    resolve: {
      // 别名配置 - 创建模块导入的快捷路径
      alias: {
        // 合并基础配置中的别名
        ...viteRenderBaseConfig.resolve?.alias,
        // 为日志模块创建别名 - 指向web平台特定实现
        "@follow/logger": resolve(__dirname, "../../packages/internal/logger/web.ts"),
      },
    },

    // 插件配置数组 - Vite功能扩展点
    plugins: [
      // 包含基础配置中的所有插件 - 类型断言确保类型兼容
      ...((viteRenderBaseConfig.plugins ?? []) as any),

      // 路由自动生成插件 - 根据文件系统自动生成路由
      routeBuilderPlugin({
        // 页面文件匹配模式 - 查找src/pages目录下的所有.tsx文件
        pagePattern: "src/pages/**/*.tsx",
        // 生成路由文件路径 - 生成的路由代码输出位置
        outputPath: "src/generated-routes.ts",
        // 在开发模式下启用 - 开发时自动更新路由
        enableInDev: true,
      }),

      // 多语言插件 - 处理应用的国际化
      localesPlugin(),

      // 条件性启用PWA插件 - 仅在Web构建时启用
      isWebBuild &&
        VitePWA({
          // PWA策略 - 使用自定义Service Worker文件
          strategies: "injectManifest",
          // 源码目录 - 查找Service Worker源文件的目录
          srcDir: "src",
          // Service Worker文件名 - 指定自定义Service Worker文件
          filename: "sw.ts",
          // 注册类型 - 提示用户决定是否安装PWA
          registerType: "prompt",
          // 不自动注入注册代码 - 手动控制PWA注册时机
          injectRegister: false,

          // 自定义Service Worker注入配置
          injectManifest: {
            // 资源匹配模式 - 哪些文件会被缓存
            globPatterns: [
              "**/*.{js,json,css,html,txt,svg,png,ico,webp,woff,woff2,ttf,eot,otf,wasm}",
            ],

            // 清单转换函数 - 自定义生成的资源清单
            manifestTransforms: [
              (manifest) => {
                return {
                  // 原始清单
                  manifest,
                  // 警告数组 - 这里没有警告
                  warnings: [],
                  // 额外的清单条目 - 添加自定义Service Worker入口
                  additionalManifestEntries: [
                    {
                      // 自定义Service Worker路径
                      url: "/sw.js?pwa=true",
                      // 版本标识 - null表示不缓存
                      revision: null,
                    },
                  ],
                }
              },
            ],
          },

          // PWA清单配置 - 定义Web应用的安装属性
          manifest: {
            // 主题色 - 应用主题颜色
            theme_color: "#000000",
            // 应用名称 - 显示在主屏幕上的名称
            name: "Folo",
            // 显示模式 - 独立应用模式
            display: "standalone",
            // 背景色 - 应用启动时的背景颜色
            background_color: "#ffffff",
            // 应用图标配置 - 不同尺寸和用途的图标
            icons: [
              {
                // 图标路径 - 小型图标
                src: "pwa-64x64.png",
                // 图标尺寸
                sizes: "64x64",
                // 图标类型
                type: "image/png",
              },
              {
                src: "pwa-192x192.png",
                sizes: "192x192",
                type: "image/png",
              },
              {
                src: "pwa-512x512.png",
                sizes: "512x512",
                type: "image/png",
              },
              {
                src: "maskable-icon-512x512.png",
                sizes: "512x512",
                type: "image/png",
                // 适用于自适应图标 - 支持各种设备上的自适应显示
                purpose: "maskable",
              },
            ],
          },

          // 开发模式下的PWA选项 - 开发环境配置
          devOptions: {
            // 禁用开发模式的PWA - 避免开发干扰
            enabled: false,
            // 导航回退页面 - 未匹配路由时回退到index.html
            navigateFallback: "index.html",
            // 抑制警告 - 减少开发时的警告信息
            suppressWarnings: true,
            // 使用ES模块 - Service Worker使用现代模块系统
            type: "module",
          },
        }),

      // 条件性启用legacy插件 - 仅在非开发模式下启用
      mode !== "development" &&
        legacy({
          // 目标浏览器 - 支持默认的旧浏览器
          targets: "defaults",
          // 不生成旧版chunk - 仅优化现代构建
          renderLegacyChunks: false,
          // 现代浏览器目标配置 - 定义现代浏览器支持范围
          modernTargets: ">0.3%, last 2 versions, Firefox ESR, not dead",
          // 现代浏览器需要的polyfill - 特定新API的兼容处理
          modernPolyfills: ["es.promise.with-resolvers"],
        }),

      // HTML注入插件 - 向HTML文件注入环境变量等信息
      htmlInjectPlugin(typedEnv),

      // 条件性启用SSL证书生成 - 当设置SSL环境变量时启用
      process.env.SSL ? mkcert() : false,

      // 自定义开发打印插件 - 显示额外的调试URL信息
      devPrint(),

      // 依赖分块插件 - 优化打包，将相关依赖分到同一chunk
      createDependencyChunksPlugin([
        // React框架相关依赖 - 核心渲染库
        ["react", "react-dom"],
        // React生态系统依赖 - 错误处理、服务端渲染和路由
        ["react-error-boundary", "react-dom/server", "react-router"],
        // 状态管理相关依赖 - 各种状态管理解决方案
        ["zustand", "jotai", "use-context-selector", "immer"],
        // Markdown解析相关依赖 - 用于处理和渲染Markdown内容
        [
          "remark-directive",
          "remark-gfm", // GitHub风格Markdown
          "remark-parse",
          "remark-stringify",
          "remark-rehype", // 转换为HTML
          "@microflash/remark-callout-directives",
          "remark-gh-alerts",
        ],
        // HTML处理相关依赖 - HTML解析和操作工具
        [
          "rehype-parse",
          "rehype-sanitize", // 安全过滤HTML
          "rehype-stringify",
          "rehype-infer-description-meta",
          "hast-util-to-jsx-runtime", // 转换为React组件
          "hast-util-to-text",
          "react-shadow", // 隔离DOM样式
        ],
        // 统一处理工具 - 处理虚拟文件
        ["vfile", "unified"],
        // ES工具库 - 提供兼容性工具
        ["es-toolkit/compat"],
        // 动画库 - React动画支持
        ["motion/react"],
        // CSS工具库 - 类名处理和合并
        ["clsx", "tailwind-merge", "class-variance-authority"],

        // UI组件库 - Radix UI组件集合
        [
          "@radix-ui/react-dialog", // 对话框组件
          "@radix-ui/react-avatar", // 头像组件
          "@radix-ui/react-checkbox", // 复选框组件
          "@radix-ui/react-context", // 上下文API
          "@radix-ui/react-dropdown-menu", // 下拉菜单
          "@radix-ui/react-hover-card", // 悬停卡片
          "@radix-ui/react-label", // 表单标签
          "@radix-ui/react-popover", // 弹出层
          "@radix-ui/react-radio-group", // 单选按钮组
          "@radix-ui/react-scroll-area", // 滚动区域
          "@radix-ui/react-select", // 选择器组件
          "@radix-ui/react-slider", // 滑块组件
          "@radix-ui/react-slot", // 内容插槽
          "@radix-ui/react-switch", // 开关组件
          "@radix-ui/react-tabs", // 标签页组件
          "@radix-ui/react-toast", // 提示组件
          "@radix-ui/react-tooltip", // 工具提示

          "@headlessui/react", // 另一个UI组件库
        ],
        // 国际化相关依赖 - 应用翻译支持
        ["i18next", "i18next-browser-languagedetector", "react-i18next"],
        // 数据查询相关依赖 - 服务器状态管理
        [
          "@tanstack/react-query", // React Query核心
          "@tanstack/react-query-persist-client", // 状态持久化
          "@tanstack/query-sync-storage-persister", // 使用localStorage持久化
        ],
        // URL解析工具 - 处理域名信息
        ["tldts"],

        // 监控和分析 - 错误跟踪和用户分析
        ["@sentry/react", "@openpanel/web"],
        // 表单和验证 - 表单处理和数据验证
        ["zod", "react-hook-form", "@hookform/resolvers"],
      ]),

      // 创建平台特定的导入插件 - 根据构建类型选择导入路径
      createPlatformSpecificImportPlugin(isWebBuild ? "web" : "electron"),
      // 条件性启用manifest插件 - 仅在Web构建时启用
      isWebBuild && manifestPlugin(),
      // 条件性启用HTML插件 - 仅在Web构建时启用
      isWebBuild && htmlPlugin(typedEnv),
      // 条件性启用打包分析 - 当设置analyzer环境变量时启用
      process.env.analyzer && analyzer(),
    ],

    // 全局变量定义 - 在编译时替换为实际值
    define: {
      // 合并基础配置中的全局变量定义
      ...viteRenderBaseConfig.define,
      // 定义ELECTRON为false - 表示当前不是Electron环境
      ELECTRON: "false",
    },
  })
}

// 浏览器兼容性检查函数
function checkBrowserSupport() {
  // 检查浏览器是否支持必要的API
  if (!("findLastIndex" in Array.prototype) || !("structuredClone" in window)) {
    // 显示兼容性警告
    window.alert(
      "Folo is not compatible with your browser because your browser version is too old. You can download and use the Folo app or continue using it with the latest browser.",
    )
    // 重定向到下载页面
    window.location.href = "https://folo.is/download"
  }
}

// HTML处理插件 - 返回一个接收环境变量的插件工厂函数
const htmlPlugin: (env: any) => PluginOption = (env) => {
  let config: ResolvedConfig // 保存解析后的配置的变量
  return {
    name: "html-transform", // 插件名称
    // 配置解析完成后的钩子
    configResolved(resolvedConfig) {
      config = resolvedConfig // 保存解析后的配置
    },
    enforce: "post", // 插件执行顺序 - 后置执行
    // 打包完成后的钩子
    closeBundle() {
      // 获取配置中的根目录和输出目录
      const { root } = config
      const dist = config.build.outDir
      const debugProxyHtml = join(root, "debug_proxy.html") // 调试代理HTML文件路径

      // 处理调试代理HTML文件
      if (existsSync(debugProxyHtml)) {
        // 读取HTML文件内容
        const content = readFileSync(debugProxyHtml, "utf-8")

        // 确保输出目录存在
        mkdirSync(dist, { recursive: true })
        // 写入修改后的HTML，替换环境变量为实际值
        writeFileSync(
          join(dist, "__debug_proxy.html"),
          content.replace("import.meta.env.VITE_API_URL", `"${env.VITE_API_URL}"`),
        )
      }
    },
    // 转换索引HTML的钩子
    transformIndexHtml(html) {
      // 使用htmlMinify压缩HTML
      return htmlMinify(
        // 替换注释为浏览器兼容性检查脚本
        html.replace(
          "<!-- Check Browser Script Inject -->",
          `<script>${checkBrowserSupport.toString()}; checkBrowserSupport()</script>`,
        ),
        // HTML压缩配置
        {
          removeComments: true, // 移除注释
          html5: true, // 启用HTML5模式
          minifyJS: true, // 压缩JS
          minifyCSS: true, // 压缩CSS
          removeTagWhitespace: true, // 移除标签空白
          collapseWhitespace: true, // 折叠空白
          collapseBooleanAttributes: true, // 折叠布尔属性
          collapseInlineTagWhitespace: true, // 折叠内联标签空白
        },
      )
    },
  }
}
